#include "JWorkingCopies.h"
#include "JVerifyValidate.h"
#include "JSerialize.h"
#include "JException.h"
#include <boost/filesystem.hpp>
#include "JLogger.h"
#include <algorithm>
#include "JDir.h"
#include <boost/exception/all.hpp>

using namespace boost::filesystem;

JWorkingCopies::JWorkingCopies()
{
	this->IsChanged = true;
	this->needInitOperation = true;
}


std::string JWorkingCopies::CreateWorkingCopies(std::string _rootUrl,std::string _author,std::string _password,std::string _fullPath)
{
	std::string errorMsg;
	try
	{
		this->rootUrl = _rootUrl;
		this->author = _author;
		this->password = _password;
		this->rootDir = _fullPath;
		this->jsvnPath = rootDir + "\\.jsvn";
		this->jsvnFileName = rootDir + "\\.jsvn\\WorkingCopies";
		CreateWorkingCopiesDir();
		CreateWorkingCopiesFile();
	}
	catch(JException& e)
	{
		errorMsg.append(*(boost::get_error_info<JErrorInfo>(e)));
	}
	return errorMsg;
}

void JWorkingCopies::BasicOperateInit( std::string currentWorkingPath )
{
	this->rootDir = JDir::RecurseUpFindFolder(currentWorkingPath,".jsvn");
	if(!this->rootDir.empty())
	{
		this->jsvnPath = rootDir + "\\.jsvn";
		this->jsvnFileName = rootDir + "\\.jsvn\\WorkingCopies";
		JFile file(this->jsvnFileName.c_str());
		this->ReSerialize(file.OpenStream(std::fstream::in | std::fstream::binary));
		file.CloseStream();
	}
	else
	{
		//throw JFileReadException() << JFileReadErrorInfo("It's not a working path.");
		throw JException() << JErrorInfo("It's not a working path.");
	}
}


JWorkingCopies::~JWorkingCopies(void)
{
	if(IsChanged == true)
	{
		SaveWorkingCopiesToFile();
	}
	
}

int JWorkingCopies::Serialize( std::iostream& _strea,int& allSize )
{
	//std::string rootUrl;
	//std::string author;
	//std::string password;
	//std::string rootDir;
	//std::list<JWorkingCopiesBaseFile> copiesFile;
	int classSize = 0;
	int selfSize = 0;
	JSerialize::PutInt(_strea,classSize,selfSize);
	JSerialize::Serialize(_strea,this->rootUrl,classSize);
	JSerialize::Serialize(_strea,this->author,classSize);
	JSerialize::Serialize(_strea,this->password,classSize);
	JSerialize::Serialize(_strea,this->rootDir,classSize);
	JSerialize::Serialize<JWorkingCopiesBaseFile>(_strea,this->copiesFile,classSize);
	selfSize += classSize;
	JSerialize::Verify(_strea,classSize,-1*selfSize);
	allSize += selfSize;
	return selfSize;
}

void JWorkingCopies::ReSerialize( std::iostream& strea )
{
	int classSize = JSerialize::GetInt(strea,sizeof(int));
	this->rootUrl = JSerialize::ReSerializeString(strea);
	this->author = JSerialize::ReSerializeString(strea);
	this->password = JSerialize::ReSerializeString(strea);
	this->rootDir = JSerialize::ReSerializeString(strea);
	//this->copiesFileName = JSerialize::ReSerializeStringList(strea);
	this->copiesFile = JSerialize::ReSerializeList<JWorkingCopiesBaseFile>(strea);
}

JOperateFileResult JWorkingCopies::AddFile( std::string fileName,bool isRecurse )
{
	JOperateFileResult result;
	try
	{
		fileName = ConvertFullFileName(fileName);
		if(needInitOperation == true)
		{
			this->BasicOperateInit(JDir::CurrentWorkingPath());
		}

		//It's in working path, so only add "fileName" to to WorkingCopies file.
		if(!IsInSubWorkingPath(fileName))
		{
			throw JException() << JErrorInfo(fileName + " is not in working copies.");
		}
		if(isRecurse == true)
		{
			std::list<std::string> listFileName = JBaseFile::RecurseDownBaseFile(fileName);
			std::list<std::string>::iterator ite = listFileName.begin();
			for(;ite != listFileName.end(); ++ite)
			{
				JWorkingCopiesBaseFile file;
				file.operate = JWorkingFileAdd;
				file.lastChangeVersion = -1;
				file.version = -1;
				file.author = this->author;
				file.fullFileName = *ite;
				if(this->copiesFile.end() == find(this->copiesFile.begin(),this->copiesFile.end(),file))
				{
					this->copiesFile.push_back(file);
					std::list<JWorkingCopiesBaseFile>::iterator ite2 = this->copiesFile.end();
					--ite2;
					result.workingCopies.push_back(&*ite2);
				}
			}
		}
		else
		{
			JWorkingCopiesBaseFile file;
			file.operate = JWorkingFileAdd;
			file.lastChangeVersion = -1;
			file.version = -1;
			file.author = this->author;
			file.fullFileName = fileName;
			std::list<JWorkingCopiesBaseFile>::iterator ite2 = this->copiesFile.end();
			--ite2;
			result.workingCopies.push_back(&*ite2);
		}
	}
	catch(JException& e)
	{
		result.errorMsg = *(boost::get_error_info<JErrorInfo>(e));
	}
	return result;
}

JOperateFileResult JWorkingCopies::AddFile( std::list<std::string> _fullFileNames )
{
	JOperateFileResult result;
	std::list<std::string>::iterator ite = _fullFileNames.begin();
	for(;ite != _fullFileNames.end(); ++ite)
	{
		JWorkingCopiesBaseFile file;
		file.operate = JWorkingFileAdd;
		file.lastChangeVersion = -1;
		file.version = -1;
		file.author = this->author;
		file.fullFileName = *ite;
		if(this->copiesFile.end() == find(this->copiesFile.begin(),this->copiesFile.end(),file))
		{
			this->copiesFile.push_back(file);
			std::list<JWorkingCopiesBaseFile>::iterator ite2 = this->copiesFile.end();
			--ite2;
			result.workingCopies.push_back(&*ite2);
		}
	}
	return result;
}

void JWorkingCopies::CreateWorkingCopiesDir()
{
	JDir::CreateDir(this->jsvnPath);
}

void JWorkingCopies::CreateWorkingCopiesFile()
{
	JFile::CreateFile(this->jsvnFileName);
}

JOperateFileResult JWorkingCopies::DeleteFile( std::string fileName )
{
	JOperateFileResult result;
	try
	{
		if(needInitOperation == true)
		{
			this->BasicOperateInit(JDir::CurrentWorkingPath());
		}
		fileName = ConvertFullFileName(fileName);
		//printf("delete file %s\n",fileName.c_str());
		if(!IsInSubWorkingPath(fileName))
		{
			throw JException() << JErrorInfo(fileName + " is not in working copies.");
		}
		std::list<std::list<JWorkingCopiesBaseFile>::iterator > listIterator = FileInPath(fileName);
		std::list<std::list<JWorkingCopiesBaseFile>::iterator >::iterator ite = listIterator.begin();
		for(;ite != listIterator.end(); ++ite)
		{
			if((*ite)->version != -1)
			{
				(*ite)->operate = JWorkingFileDelete;
				result.workingCopies.push_back(&**ite);
			}
			else
			{
				(**ite).operate = JWorkingFileDelete;
				JWorkingCopiesBaseFile file(**ite);
				this->copiesFile.erase(*ite);
				result.originalNotInWorkingCopies.push_back(file);
			}
		}
	}
	catch(JException& e)
	{
		result.errorMsg = *(boost::get_error_info<JErrorInfo>(e));
	}
	return result;
}

JOperateFileResult JWorkingCopies::ModifyFile( std::string oldFileName,std::string newFileName )
{
	oldFileName = JDir::Absolute(oldFileName);
	newFileName = JDir::Absolute(newFileName);
	JOperateFileResult result = DeleteFile(oldFileName);
	JBaseFile::ReName(oldFileName,newFileName);
	needInitOperation = false;
	
	std::list<std::string> fullFileName;
	std::list<JWorkingCopiesBaseFile*>::iterator ite = result.workingCopies.begin();
	for(;ite != result.workingCopies.end(); ++ite)
	{
		fullFileName.push_back(newFileName + "\\" + (*ite)->fullFileName.substr(oldFileName.length()));
	}

	std::list<JWorkingCopiesBaseFile>::iterator ite2 = result.originalNotInWorkingCopies.begin();
	for(;ite2 != result.originalNotInWorkingCopies.end(); ++ite2)
	{
		fullFileName.push_back(newFileName + ite2->fullFileName.substr(oldFileName.length()));
	}
	JOperateFileResult result2 = AddFile(fullFileName);
	result += result2;
	return result;
}

//bool JWorkingCopies::IsInWorkingCopies( std::string fileName ) 
//{
//	if(this->copiesFileName.end() != find(this->copiesFileName.begin(),this->copiesFileName.end(),fileName))
//	{
//		return true;
//	}
//	return false;
//}

//std::list<std::string> JWorkingCopies::GetAllFileNames()
//{
//	return this->copiesFileName;
//}

std::string JWorkingCopies::FindRootDir(std::string currentWorkingPath)
{
	std::string rootPath = JDir::RecurseUpFindFolder(currentWorkingPath,".jsvn");
	return rootPath;
}

std::string JWorkingCopies::ConvertFullFileName( std::string _fileName )
{
		if(JBaseFile::IsExist(_fileName))
		{
			return JBaseFile::Absolute(_fileName);
		}
		else
		{
			return _fileName;
			//throw JFileNotExistException() << JFileNotExistErrorInfo("Function:ConvertFullFileName error.File not exist.");
			//throw JException() << JErrorInfo("Function:ConvertFullFileName error.File not exist.");
		}
}

bool JWorkingCopies::IsInWorkingPath( std::string _fileName )
{
	if(_fileName.find(this->rootDir) == std::string::npos)
	{
		return false;
	}
	return true;
}

std::string JWorkingCopies::ConvertToFullWorkingFileName( std::string _fullFileName )
{
	path p(_fullFileName);
        std::string s = p.filename().generic_string();
	std::string ss = boost::filesystem::current_path().generic_string();
	return ss + "\\" + s;
}

bool JWorkingCopies::IsInSubWorkingPath( std::string fullFileName )
{
	return JBaseFile::HasGenerateFile(this->rootDir,fullFileName);
}

void JWorkingCopies::SaveWorkingCopiesToFile()
{
	JFile file(this->jsvnFileName);
	int tmp;
	Serialize(file.OpenStream(std::fstream::out | std::fstream::binary | std::fstream::trunc),tmp);
	file.CloseStream();
}


JOperateFileResult JWorkingCopies::FileStatus( std::string fileName )
{
	JOperateFileResult reslut;
	try
	{
		this->IsChanged = false;
		fileName = ConvertFullFileName(fileName);
		this->BasicOperateInit(fileName);
		std::list<JWorkingCopiesBaseFile>::iterator ite = this->copiesFile.begin();
		for(;ite != this->copiesFile.end(); ++ite)
		{
			if(IsBaseFileValidate(ite->fullFileName))
			{
				reslut.workingCopies.push_back(&(*ite));
			}
			else
			{
				ite->operate = JWorkingFileNotExist;
				reslut.workingCopies.push_back(&(*ite));
			}
		}
	}
	catch(JException& e)
	{
		reslut.errorMsg.append(*boost::get_error_info<JErrorInfo>(e));
	}
	return reslut;
}


std::list<std::list<JWorkingCopiesBaseFile>::iterator > JWorkingCopies::FileInPath( std::string _filePath )
{
	std::list<std::list<JWorkingCopiesBaseFile>::iterator > listIterator;
	std::list<JWorkingCopiesBaseFile>::iterator ite = this->copiesFile.begin();
	for(;ite != this->copiesFile.end(); ++ite)
	{
		if(JDir::HasGenerateFile(_filePath,ite->fullFileName))
		{
			listIterator.push_back(ite);
		}
	}
	return listIterator;
}

bool JWorkingCopies::IsBaseFileValidate( std::string _fullFileName )
{
	return JBaseFile::IsExist(_fullFileName);
}

int JWorkingCopies::SerializeWithFile( std::iostream& _strea,int& allSize )
{
	//std::string rootUrl;
	//std::string author;
	//std::string password;
	//std::string rootDir;
	//std::list<JWorkingCopiesBaseFile> copiesFile;
	//if JWorkingCopiesBaseFile is a dir, serialize
	int classSize = 0;
	int selfSize = 0;
	JSerialize::PutInt(_strea,classSize,selfSize);
	JSerialize::Serialize(_strea,this->rootUrl,classSize);
	JSerialize::Serialize(_strea,this->author,classSize);
	JSerialize::Serialize(_strea,this->password,classSize);
	JSerialize::Serialize(_strea,this->rootDir,classSize);
	JSerialize::Serialize<JWorkingCopiesBaseFile>(_strea,this->copiesFile,classSize);
	selfSize += classSize;
	JSerialize::Verify(_strea,classSize,-1*selfSize);
	allSize += selfSize;
	return selfSize;
}
